#pragma once
#include "util.hpp"

typedef enum
{
    LOGIN,
    UNLOGIN
} ss_statu;

class session
{
private:
    uint64_t _ssid;            // 标识
    uint64_t _uid;             // 用户id
    ss_statu _statu;           // 状态
    wsserver_t::timer_ptr _tp; // 定时器
public:
    session(uint64_t ssid):_ssid(ssid) { DLOG("session %p 被创建!", _ssid); }
    ~session() { DLOG("session %p 被销毁!", _ssid); }
    void set_user(uint64_t uid) { _uid = uid; }
    void set_statu(ss_statu statu) { _statu = statu; }
    void set_timer(const wsserver_t::timer_ptr &tp) { _tp = tp; }
    bool is_login() { return _statu == LOGIN; }
    uint64_t get_ssid() { return _ssid; }
    uint64_t get_uid() { return _uid; }
    wsserver_t::timer_ptr &get_timer() { return _tp; }
};

using session_ptr = std::shared_ptr<session>;
#define SESSION_TIMEOUT 30000
#define SESSION_FOREVER -1

class session_manager
{
private:
    uint64_t _next_sid;
    std::mutex _mutex;
    std::unordered_map<uint64_t, session_ptr> _session;
    wsserver_t *_svr;

public:
    session_manager(wsserver_t *svr) : _next_sid(1), _svr(svr) { DLOG("session管理器创建成功!"); }
    ~session_manager() { DLOG("session管理器即将销毁!"); }
    // 创建session
    session_ptr session_create(uint64_t uid, ss_statu statu)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        session_ptr sp(new session(_next_sid));
        sp->set_statu(statu);
        sp->set_user(uid);
        _session.insert(make_pair(_next_sid, sp));
        ++_next_sid;
        return sp;
    }
    // 通过sid来获取session
    session_ptr get_session_by_sid(uint64_t sid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _session.find(sid);
        if (it == _session.end())
        {
            return session_ptr();
        }
        return it->second;
    }
    // 新增session
    void append_session(session_ptr sp)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _session.insert(std::make_pair(sp->get_ssid(), sp));
    }
    // 删除session
    void remove_session(uint64_t sid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _session.erase(sid);
    }
    // 通过设置定时任务来对session生命周期的管理
    void set_session_expire_time(uint64_t sid, int ms)
    {
        // http通信时为短连接，该通信是定时删除的
        // websocket通信是长连接，session是永久存在的
        // 注册或者登陆的时候应该是短连接通信，应该定时删除，当用户退出游戏大厅或者游戏房间的时候应该设置定时删除
        session_ptr sp = get_session_by_sid(sid);
        if (sp.get() == nullptr)
            return;
        wsserver_t::timer_ptr tp = sp->get_timer();
        if (tp.get() == nullptr && ms == SESSION_FOREVER)
        {
            // 1.在session永久存在下设置永久存在
            return;
        }
        else if (tp.get() == nullptr && ms == SESSION_TIMEOUT)
        {
            // 2.在session永久存在下设置定时删除
            wsserver_t::timer_ptr tmp_tp = _svr->set_timer(ms, std::bind(&session_manager::remove_session, this, sid));
            sp->set_timer(tmp_tp);
        }
        else if (tp.get() != nullptr && ms == SESSION_FOREVER)
        {
            // 3.在定时删除的情况下设置永久存在
            // 因为取消定制删除就会立即执行，所以我们在取消之后要添加sid和session的映射关系
            tp->cancel();
            // 清空定制删除
            sp->set_timer(wsserver_t::timer_ptr());
            // 重新设置
            _svr->set_timer(0, std::bind(&session_manager::append_session, this, sp));
        }
        else if (tp.get() != nullptr && ms == SESSION_TIMEOUT)
        {
            // 4.在定时删除的情况下，重新计算时间定时删除
            tp->cancel();
            sp->set_timer(wsserver_t::timer_ptr());
            _svr->set_timer(0, std::bind(&session_manager::append_session, this, sp));
            //重新设置定时任务
             wsserver_t::timer_ptr timer_tp = _svr->set_timer(ms, std::bind(&session_manager::remove_session, this, sid));
            sp->set_timer(timer_tp);
        }
    }
};